package xgorm

import (
	"errors"
	"fmt"
	"gitee.com/igolang/pkg/xjson"
	"gorm.io/gorm"
	"strings"
)

type Callback struct {
	Name      string
	Action    string
	SortName  string
	SortValue string
	Callback  func(db *gorm.DB) `json:"-"`
}

func (c Callback) Valid() error {
	if c.Name == "" {
		return errors.New("db callback name is nil")
	}

	if c.Callback == nil {
		return errors.New("db callback is nil")
	}

	if c.Action == "" {
		return errors.New("db action is nil")
	}

	return nil
}

func SetCallBacks(db *gorm.DB, callbacks ...Callback) error {
	if db == nil || len(callbacks) == 0 {
		return nil
	}
	b, _ := xjson.Marshal(callbacks)
	db.Logger.Info(db.Statement.Context, fmt.Sprintf("SetCallBacks: %s", string(b)))
	errs := make([]error, 0)
	for _, callback := range callbacks {
		if err := callback.Valid(); err != nil {
			errs = append(errs, fmt.Errorf("callback: %s, err: %v", callback.Name, err))
			continue
		}

		var processor = db.Callback().Create()

		if strings.ToLower(callback.Action) == "update" {
			processor = db.Callback().Update()
		}

		if strings.ToLower(callback.Action) == "delete" {
			processor = db.Callback().Delete()
		}
		if strings.ToLower(callback.Action) == "query" {
			processor = db.Callback().Query()
		}
		if strings.ToLower(callback.Action) == "row" {
			processor = db.Callback().Row()
		}
		if strings.ToLower(callback.Action) == "raw" {
			processor = db.Callback().Raw()
		}
		// 校验action的合法性
		if processor == db.Callback().Create() && strings.ToLower(callback.Action) != "create" {
			errs = append(errs, fmt.Errorf("callback: %s, db action invalid", callback.Name))
			continue
		}

		var exist bool
		if processor.Get(callback.Name) != nil {
			exist = true
			//errs = append(errs, fmt.Errorf("callback: %s, already registered", callback.Name))
			//continue
		}

		name := strings.ToLower(callback.SortName)
		var err error
		if name == "before" {
			if exist {
				err = processor.Before(callback.SortValue).Replace(callback.Name, callback.Callback)
			} else {
				err = processor.Before(callback.SortValue).Register(callback.Name, callback.Callback)
			}
		} else if name == "after" {
			if exist {
				err = processor.After(callback.SortValue).Replace(callback.Name, callback.Callback)
			} else {
				err = processor.After(callback.SortValue).Register(callback.Name, callback.Callback)
			}
		} else {
			if exist {
				err = processor.Replace(callback.Name, callback.Callback)
			} else {
				err = processor.Register(callback.Name, callback.Callback)
			}
		}

		if err != nil {
			errs = append(errs, err)
		}
	}

	if len(errs) > 0 {
		return errors.Join(errs...)
	}

	return nil
}
